import axios, { AxiosInstance, AxiosRequestConfig } from "axios";

// 对 AxiosConfigRequest 做扩展
declare module "axios" {
  interface AxiosRequestConfig {
    loading?: boolean;
    cancel?: boolean;
  }
}

let pendingMap = new Map<string, AbortController>();

export const getPendingUrl = (config: AxiosRequestConfig) =>
  [
    config.method,
    config.url,
    // JSON.stringify(config.data),
    // JSON.stringify(config.params),
  ].join("&");

export class AxiosCanceler {
  /**
   * @description: 添加请求
   * @param {Object} config
   * @return void
   */
  addPending(config: AxiosRequestConfig) {
    // debugger
    // 在请求开始前，对之前的请求做检查取消操作
    this.removePending(config);
    const url = getPendingUrl(config);
    const controller = new AbortController();
    config.signal = controller.signal;
    pendingMap.set(url, controller);
  }

  /**
   * @description: 移除请求
   * @param {Object} config
   */
  removePending(config: AxiosRequestConfig) {
    const url = getPendingUrl(config);
    // 如果在 pending 中存在当前请求标识，需要取消当前请求
    const controller = pendingMap.get(url);
    controller && controller.abort();
  }

  /**
   * @description: 清空所有pending
   */
  removeAllPending() {
    pendingMap.forEach((controller) => {
      controller && controller.abort();
    });
    pendingMap.clear();
  }
}

// new AxiosCanceler
const axiosCanceler = new AxiosCanceler();

// 默认请求配置
const config: AxiosRequestConfig = {
  // 默认地址请求地址
  baseURL: "http://localhost:3000/",
  // 设置超时时间
  timeout: 1000 * 10,
};

class RequestHttp {
  server: AxiosInstance;

  constructor(config: AxiosRequestConfig) {
    // 创建 axios 实例
    this.server = axios.create(config);

    // 请求拦截
    this.server.interceptors.request.use((config) => {
      config.cancel ?? (config.cancel = true);
      config.cancel && axiosCanceler.addPending(config);
      return config;
    });

    // 响应拦截
    this.server.interceptors.response.use((response) => {
      axiosCanceler.removePending(response.config);

      return response;
    });
  }
}

export default new RequestHttp(config);
